use std::sync::Arc;

use ecksport_core::frame;

use aspect_ident::{
    device::{self, DeviceAttestation},
    signer, IdentSigner,
};

#[derive(Clone)]
pub struct AspAuthConfig {
    signer: Option<Arc<IdentSigner>>,
}

impl AspAuthConfig {
    // TODO make a constructor that takes the inner data or something?
    pub fn new_auth(signer: Arc<IdentSigner>) -> Self {
        Self {
            signer: Some(signer),
        }
    }

    pub fn new_unauth() -> Self {
        Self { signer: None }
    }
}

impl ecksport_core::traits::AuthConfig for AspAuthConfig {
    fn get_intent(&self) -> frame::AuthIntent {
        frame::AuthIntent::ClientOnly
    }

    fn sign_challenge(
        &self,
        client_chal: &frame::ChallengeData,
        server_chal: &frame::ChallengeData,
        side: frame::Side,
    ) -> Result<Option<frame::ResponseData>, ecksport_core::errors::AuthError> {
        let Some(signer) = &self.signer else {
            return Ok(None);
        };

        let msg = compute_chal_buf(client_chal, server_chal, side);
        let attestation = signer.create_device_attestation(&msg);

        let payload_enc = aspect_codec::encode_to_vec(&attestation).expect("auth: make payload");
        let resp = frame::ResponseData::new(payload_enc);
        Ok(Some(resp))
    }

    fn verify_response(
        &self,
        client_chal: &frame::ChallengeData,
        server_chal: &frame::ChallengeData,
        producer_side: frame::Side,
        resp: &frame::ResponseData,
    ) -> Result<Option<ecksport_core::peer::Identity>, ecksport_core::errors::AuthError> {
        let attestation = aspect_codec::decode::<DeviceAttestation>(resp.data())
            .map_err(|_| ecksport_core::errors::AuthError::MalformedResp)?;

        let msg = compute_chal_buf(client_chal, server_chal, producer_side);

        let mut ok = true;
        ok &= signer::verify_ann_package_integrity(attestation.ann_package());
        ok &= device::verify_attestation(&attestation, &msg);

        if ok {
            let ann = attestation
                .ann_package()
                .try_decode()
                .expect("auth: ann malformed after successful verification");
            let ident = ann.get_ident();
            let custom_key = ecksport_core::peer::CustomKeyId::from(*ident.as_ref());
            let ident = ecksport_core::peer::Identity::CustomKey(custom_key);
            Ok(Some(ident))
        } else {
            Err(ecksport_core::errors::AuthError::InvalidResp)
        }
    }
}

fn compute_chal_buf(
    cc: &frame::ChallengeData,
    sc: &frame::ChallengeData,
    producer_side: frame::Side,
) -> Vec<u8> {
    let mut buf = Vec::new();

    buf.extend_from_slice(cc.nonce());
    buf.extend_from_slice(sc.nonce());

    buf.extend_from_slice(
        match producer_side {
            frame::Side::Client => "client",
            frame::Side::Server => "server",
        }
        .as_bytes(),
    );

    buf
}
